#region File Description
/* SmlProgram.cs
 * Sets up and runs SML
 * Version 1.1
 * */
#endregion

#region Using Statements

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using Sml.Screens;
using SmlEngine.UI.Input;
using SmlEngine.UI.Managers;
using SmlEngine.UI.Screens;

#endregion

namespace Sml
{
    /// <summary>
    /// This is the class that sets up and runs SML.
    /// </summary>
    public class SmlProgram : Game
    {
        #region Fields

        private const string Title = "SML v0.04 - Alpha";

        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        private ScreenManager screenManager;

        private List<Screen> Screens; // used to store screens for now because I can't see how to make a screen inactive in ScreenManager yet

        private TitleScreen titleScreen;
        private MainMenuScreen mainMenuScreen;
        private GameScreen gameScreen;

        private WorldScreen worldScreen;

        private int worldIndex = -1; // used when entering levels to keep track of which world is being used
        private int worldSectionIndex = -1; // which section the world is above
        private List<int> worldExitNumber = new List<int>(); // which exits this level will trigger on clear
        #endregion

        #region Methods

        /// <summary>
        /// Constructs a new SmlProgram instance.
        /// </summary>
        public SmlProgram()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            Screens = new List<Screen>();
            graphics.DeviceReset += new EventHandler<EventArgs>(graphics_DeviceReset);
        }

        /// <summary>
        /// Loads a level file, world file, or project file.
        /// </summary>
        /// <param name="filePath">The path of the file to open.</param>
        /// <returns>Success state.</returns>
        private bool OpenFile(string filePath)
        {
            // First, determine what type of file this is.
            // Currently, this is inferred via file extension,
            // but this will change once file handling is more solidifed.
            if (File.Exists(filePath))
            {
                string extension = Path.GetExtension(filePath);
                if (extension == ".lvl")
                {
                    TitleScreen t = new TitleScreen();
                    screenManager.SetActiveGroup(screenManager.AddScreenGroup(new ScreenGroup(screenManager, t)));
                    foreach (Sml.Scenes.LevelScene scene in t.Scenes)
                    {
                        scene.Level.LevelCleared += new SmlEngine.Sprites.Collections.LevelClearedEventHandler(Level_LevelCleared);
                    }
                    return true;
                }
                else if (extension == ".wld")
                {
                    WorldScreen w = new WorldScreen(new List<string>() { filePath, Directory.GetCurrentDirectory() + @"\resources\world4.wld" }, new Vector2(Window.ClientBounds.Height, Window.ClientBounds.Width));
                    screenManager.SetActiveGroup(screenManager.AddScreenGroup(new ScreenGroup(screenManager, w)));
                    foreach (Sml.Scenes.WorldScene scene in w.Scenes)
                    {
                        foreach (SmlEngine.Sprites.Collections.World world in scene.Worlds)
                        {
                            world.LevelEntered += new SmlEngine.Sprites.Collections.LevelEnteredEventHandler(world_LevelEntered);
                        }
                    }
                    return true;
                }
                // eventual project file handling code here
                else
                {
                    // We really need an error handling method that does not involve exceptions.
                    // ERROR
                    throw new Exception(string.Format("The file at {0} is not a valid level, project, or world file.", filePath));
                }
            }
            else
            {
                // ERROR
                throw new Exception(string.Format("The file at {0} does not exist.  Please check your paths.", filePath));
            }
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // Add initialization logic here
            Window.Title = Title;
            IsMouseVisible = true;

            //Initialize components
            InputSettings.Defaults();

            screenManager = new ScreenManager(this, "default");

            screenManager.Initialize();

            //Initialize screens
//            titleScreen = new TitleScreen(); //TitleScreen
//            screenManager.AddScreen(titleScreen);

            mainMenuScreen = new MainMenuScreen(); //MainMenuScreen
            gameScreen = new GameScreen(); //GameScreen

//            worldScreen = new WorldScreen(new List<String>(){@"D:\Documents\Files\SML\world4.wld"}, new Vector2(Window.ClientBounds.Height, Window.ClientBounds.Width)); //WorldScreen
            //screenManager.AddScreen(worldScreen);
            if (OpenFile(Directory.GetCurrentDirectory() + @"\resources\world3.wld") == null)
            {
                // null is the current fail condition because exceptions stop a method
                TitleScreen t = new TitleScreen(); // just for now we'll do this
                screenManager.SetActiveGroup(screenManager.AddScreenGroup(new ScreenGroup(screenManager, t)));
                Screens.Add(t);
            }

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Use this.Content to load game content here.
            mainMenuScreen.LoadContent(Content);
            gameScreen.LoadContent(Content);

            foreach (Screen s in screenManager.ScreenGroups[screenManager.activeGroupIndex].Screens)
            {
                //s.LoadContent(Content);
            }
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // Unload non-ContentManager content here
        }

        void graphics_DeviceReset(object sender, EventArgs e)
        {
            foreach (Screen s in screenManager.ScreenGroups[screenManager.activeGroupIndex].Screens)
            {
                if (s is WorldScreen)
                {
                    (s as WorldScreen).Scenes[0].ReloadContent();
                }
            }
        }

        void world_LevelEntered(object sender, SmlEngine.Sprites.Base.WorldMap.LevelEnteredEventArgs e)
        {
            if (!File.Exists(e.filePath))
            {
                System.Windows.Forms.MessageBox.Show(string.Format("The following level file could not be found:{0}{1}", Environment.NewLine, e.filePath), "File Not Found", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
                return;
            }
            try
            {
                foreach (Screen s in screenManager.ScreenGroups[screenManager.activeGroupIndex].Screens)
                {
                    if (s is WorldScreen)
                    {
                        foreach (Sml.Scenes.WorldScene scene in s.Scenes)
                        {
                            if (scene.Worlds.Contains(sender))
                            {
                                worldIndex = screenManager.GroupIndexFromScreen(s);
                                worldSectionIndex = scene.Worlds.IndexOf((SmlEngine.Sprites.Collections.World)sender);
                                continue;
                            }
                        }
                    }
                }
                worldExitNumber = e.exitNumber;
                OpenFile(e.filePath);
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }

        void Level_LevelCleared(object sender, SmlEngine.Sprites.Collections.LevelClearedEventArgs e)
        {
            if (screenManager.resourceState == SmlEngine.ResourceState.WorldStandalone)
            {
                screenManager.SetActiveGroup(worldIndex);
                (screenManager.ScreenGroups[worldIndex].Screens[0] as WorldScreen).GetScene(0).SwapWorld(worldSectionIndex);
                if (worldExitNumber.Any())
                {
                    (screenManager.ScreenGroups[worldIndex].Screens[0] as WorldScreen).GetScene(0).Worlds[worldSectionIndex].StartReveal(worldExitNumber[0]);
                }
                else
                {
                    (screenManager.ScreenGroups[worldIndex].Screens[0] as WorldScreen).GetScene(0).Worlds[worldSectionIndex].UnlockTile();
                }
            }
            else
            {
                // Default case for no world loaded (i.e. standalone level)
                screenManager.RemoveAllScreenGroups();
                TitleScreen t = new TitleScreen();
                screenManager.SetActiveGroup(screenManager.AddScreenGroup(new ScreenGroup(screenManager, t)));
            }
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // Add update logic here
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // Add drawing code here

             base.Draw(gameTime);
            //worldScreen.Draw(gameTime);
        }
        #endregion
    }

    #region Entry Point

#if WINDOWS || XBOX
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main(string[] args)
        {
            using (SmlProgram game = new SmlProgram())
            {
                game.Run();
            }
        }
    }
#endif

    #endregion
}
